#include <ObjectSubSystem/BaseObject.h>
#include <ObjectSubSystem/BaseCameraObject.h>
#include <ObjectSubSystem/ObjectSubSystem.h>
#include <Math/Bitfield.h>

namespace cvObjects
{
BaseCameraObject::BaseCameraObject() :
	m_offset(0.5f, 0.5f),
	m_zoom(1.0f, 1.0f),
	m_localzoom(1.0f, 1.0f),
	fn_updater(NULL),
	fn_viewport(&BaseCameraObject::viewport_glb),
	fn_zoom(&BaseCameraObject::zoom_glb) {}


int BaseCameraObject::objectType() const	{ return GAMEOBJECT_CAMERA; }

void BaseCameraObject::setRelativity(cvMath::bitfield_t flags)
{
	if(flags == OBJ_DEFAULT)	{ m_relativityflags = OBJ_XY; } else { m_relativityflags = flags; }

	if(getParent()->objectType() == GAMEOBJECT_CAMERA)
	{
		if(cvMath::someFlags_on(m_relativityflags, OBJ_VIEWPORT_XYWH))
		{
			fn_viewport = &BaseCameraObject::viewport_local;
		}
		else
		{
			fn_viewport = &BaseCameraObject::viewport_glb;
		}

		switch(cvMath::filterFlags(m_relativityflags, OBJ_ZOOM_XY))
		{
			case OBJ_ZOOM_XY:	fn_zoom = &BaseCameraObject::zoom_local; break;
			case OBJ_ZOOM_X:	fn_zoom = &BaseCameraObject::zoom_localx; break;
			case OBJ_ZOOM_Y:	fn_zoom = &BaseCameraObject::zoom_localy; break;
			default:			fn_zoom = &BaseCameraObject::zoom_glb;
		}
	}
	else
	{
		fn_updater = NULL;
	}


	cvMath::setFlag_off(flags, OBJ_VIEWPORT_XYWH | OBJ_ZOOM_XY);
	EmptyObject::setRelativity(flags);
}

void BaseCameraObject::updater()
{
	EmptyObject::updater();
	if(fn_updater)
	{
		(this->*fn_updater)();
	}

}

void BaseCameraObject::viewport_zoom_update()
{
	const BaseCameraObject* obj = static_cast<BaseCameraObject*>(getParent());
	m_viewport = obj->m_viewport + m_localviewport;
	m_zoom = obj->m_localzoom * m_localzoom;
}

void BaseCameraObject::viewport_update()
{
	m_viewport = static_cast<BaseCameraObject*>(getParent())->m_viewport + m_localviewport;
}

void BaseCameraObject::zoom_update()
{
	m_zoom = static_cast<BaseCameraObject*>(getParent())->m_zoom * m_localzoom;
}

/// Setters
void BaseCameraObject::viewport(int x, int y, int w, int h)	{ (this->*fn_viewport)(x,y,w,h); }	// *public method*
void BaseCameraObject::viewport_glb(int x, int y, int w, int h)
{
	m_viewport.l = x;
	m_viewport.t = y;
	m_viewport.r = w;
	m_viewport.b = h;
}
void BaseCameraObject::viewport_local(int x, int y, int w, int h)
{
	m_localviewport.l = x;
	m_localviewport.t = y;
	m_localviewport.r = w;
	m_localviewport.b = h;
	cvMath::setFlag_off(m_validrelatives, OBJ_VIEWPORT_XYWH);
}

void BaseCameraObject::zoomin(float factor, float x, float y)
{
	zoom(1.0, m_zoom.x - (x + factor), m_zoom.y - (y + factor));
}

void BaseCameraObject::zoomout(float factor, float x, float y)
{
	zoom(1.0, m_zoom.x + (x + factor), m_zoom.y + (y + factor));
}

void BaseCameraObject::zoom(float factor, float x, float y)	// *public method*
{
	(this->*fn_zoom)(cvMath::Vec2f(factor * x, factor * y));
}
void BaseCameraObject::zoom_glb(const cvMath::Vec2f &z)	{ m_zoom = z; }
void BaseCameraObject::zoom_local(const cvMath::Vec2f &z)
{
	m_localzoom = z; cvMath::setFlag_off(m_validrelatives, OBJ_ZOOM_XY);
}
void BaseCameraObject::zoom_localx(const cvMath::Vec2f &z)
{
	m_localzoom.x = z.x;
	m_zoom.y = z.y;
	cvMath::setFlag_off(m_validrelatives, OBJ_ZOOM_X);
}
void BaseCameraObject::zoom_localy(const cvMath::Vec2f &z)
{
	m_zoom.x = z.x;
	m_localzoom.y = z.y;
	cvMath::setFlag_off(m_validrelatives, OBJ_ZOOM_Y);
}


void BaseCameraObject::offset(const cvMath::Vec2f &p)	{ m_offset = p; }

/// Getters
const cvMath::Recti& BaseCameraObject::viewport()
{
	update_linear();
	return m_viewport;
}

const cvMath::Vec2f& BaseCameraObject::zoom()
{
	update_linear();
	return m_zoom;
}

const cvMath::Vec2f& BaseCameraObject::offset()	{ return m_offset; }

};
